Friday Code and Notes (Week 3)
Table of Contents
1 Promela code
Test-and-test-and-ste in Promela:
#include "critical2.h" bit shared = 0 inline test_and_set(x,y) { d_step { x = y; y = 1; } } proctype P() { bit local = 1; do :: non_critical_section() do :: /* await */ shared == 0; test_and_set(local,shared) if local -> break; else -> skip fi od; critical_section(); shared = 0; od }
2 Notes
first: p reads nq (currently 0) then: q reads np (currently 0) q writes 1 to nq q enters the CS p writes 1 to np p at the await check: 1 = 0 ∨ 1 ≤ 1 p enters the CS Idea: once you're past p6: await choosing[j] = false one of two things will hold: - either, j has already chosen a ticket number - or: they're somewhere in the non-CS, and may choose a ticket number in future. if they do: they will read your current ticket number therefore: if they choose a ticket number, it'll be at least 1 bigger than yours Q: while you're going through max, they've already read yours before you update it to your current #. A: yes they can; if so, they might get a smaller ticket number. but the await on p6 makes sure that you know their ticket number before you decide whether you go before them or not. Lamport's objection: Both Peterson and Dekker assume that reads and writes to single memory locations are already atomic. Arguably: a version of the critical section problem has already been solved on the hardware level. Suppose that process i is executing line p7 to read number[j] Meanwhile: j is writing its ticket number to number[j] In Lamport's setting, process i can read any value. #1: we read a ticket number that's too low we don't care: we'll just wait a bit longer #1: we read a ticket number that's too high if so: because we're past await choosing[j] = false therefore: since they're still choosing, they must have started choosing after we wrote our ticket number therefore: they've read our ticket number, and they'll end up with a ticket number larger than ours anyway Writings of Leslie Lamport: - https://lamport.azurewebsites.net/pubs/pubs.html Q: Why is the algorithm 1.6 almost correct? p1p2q1q2 - gate1 = q gate2 = 0 p@p3 q@q3 p3p4 - gate1 = q gate2 = p p5 - p is in the CS q3 - gate1 = q gate2 = q q4 - q is in the CS For the generalisation to n processes, see Lamport's paper Let's try to find a counterexample to eventual entry p1q1q2q3p2q4q6 p1q1q2q3p2q4q6 p1q1q2q3p2q4q6 p1q1q2q3p2q4q6 here: - q enters the CS infinitely often - p writes its name on the first sign infinitely often, it's always instantly overwritten - p reads second sign infinitely often, but always right after q wrote its name there hence: a counterexample to eventual entry under weak fairness. the algorithm *does* satisfy a related property, sometimes called deadlock freedom. (not to be confused w/ deadlock freedom in the sense of "noone can do anything") livelock: we don't have deadlock, there's always *something* that can happen, but what happens happens not to be productive. Livelock freedom: □(p@p1 ⇒ ◇(p@cs ∨ q@cs)) Q: does the fast algorithm satisfies the property mentioned in the first slide? There are still n awaits A: yes. O(1) pre-protocol in the absence of contention. Contention means: more than one process in the preprotocol at a time You won't hit the awaits in the absence of contention. Waiting room door: - every process can "hold the door" if a single process wants the door to be closed, it's closed. the door is open if nobody is holding it. flag[i] = 0 - I'm in the non-CS flag[i] = 1 - I want to enter the waiting room I'm in the process of trying to enter the waiting room. flag[i] = 2 - I'm already in the waiting room. But I'm not closing the door. flag[i] = 3 - I just entered the waiting room, and I'm holder the door closed. flag[i] = 4 - I'm in the waiting room (or the CS) I'm holding the door closed. I will make sure the door stays closed until the waiting room empties. Intuition: the await at line p3 represents the door to the waiting room. Intention: await ∀j. flag[j] < 3 means await flag[0] < 3 await flag[1] < 3 ... await flag[n-1] < 3 Depending on what order you run these sequentialised awaits in, the algorithm is either correct or not. Both XC and TS solutions sacrifice eventual entry. they have mutex, livelock free In TS solution. If there's contention, we're spinning on a TS. that means we're flooding the bus with writes